/**
  ******************************************************************************
  * @file    FaultIndication.c
  * @author  MIJI
  * @version V0.0.1
  * @date    16-06-2016
  * @brief   This file contains all the Fault report function
  *
  *
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; KION(SZ) R&D Center
  *
  * Licensed under KION, (the "License");
  *
  ******************************************************************************
  */
/* Includes ------------------------------------------------------------------*/
#include <string.h>
#include "FaultIndication.h"
#include "Menu.h"
#include "outputs.h"

FaultQueue_t FaultQueue;
FaultQueue_t FaultOutputQueue;
FaultQueueElement_t SingleFault = {0};

/**
* @brief    Initialize head and tail position
* @param    queue: pointer to queue instance
* @note     none
* @retval   none
*/
void FaultQueueInit(FaultQueue_t * queue)
{
    // initialize position
    queue->pFQIn = &(queue->element[0]);
    queue->pFQOut = &(queue->element[0]);   // no error codes
    SingleFault.attr |= ACK_BIT;
}

/**
* @brief    Append in queue head
* @param    queue: pointer to queue instance
* @note     none
* @retval   none
*/
u8 FaultQueueAppend(FaultQueue_t * queue, u32 faultcode1, u32 faultcode2, u8 text1, u8 text2, u8 fault_index,FaultPreActionFunc faultPreAction, FaultActionFunc faultAction)
{
    u8 ret;
    FaultQueueElement_t * in;
    FaultQueueElement_t * out;
    bool append = TRUE;

    FAULT_QUEUE_ENTER_CRITICAL;

    if (queue->pFQIn != queue->pFQOut) // there is already error codes, to find the same code
    {
        out = queue->pFQOut;
        while (out != queue->pFQIn)
        {
            if ((out->faultCode1 == faultcode1) && (out->faultCode2 == faultcode2)
                 && (out->errText1 == text1)    && (out->errText2 == text2))
            {
                append = FALSE;
                break;
            }
            out ++;
            if(out > &(queue->element[FAULT_QUEUE_SIZE - 1])) 
            {
                out = &(queue->element[0]);
            }
        }
    }
    if (append)
    {
        in = queue->pFQIn;
        in ++;
        if (in > &(queue->element[FAULT_QUEUE_SIZE - 1])) 
        {
            in = &(queue->element[0]);
        }
        if(in == queue->pFQOut) // queue full
        {
            ret = 0xFF; 
        }
        else
        {
            //insert element content
            queue->pFQIn->faultCode1 = faultcode1;
            queue->pFQIn->faultCode2 = faultcode2;
            queue->pFQIn->errText1 = text1;
            queue->pFQIn->errText2 = text2;
            queue->pFQIn->attr  &= ~(ACK_BIT | CLR_BIT | SHOW_BIT);
            queue->pFQIn->fault_index = fault_index;
            queue->pFQIn->faultPreAckAction = faultPreAction;
            queue->pFQIn->faultAckAction = faultAction;
            queue->pFQIn = in; // pointer to next
            ret = 0;
        }
    }
    else
    {
        ret = 0xFF;
    }
    FAULT_QUEUE_EXIT_CRITICAL;
    return ret;
}

/**
* @brief    Update single fault
* @param    queue: pointer to queue instance
* @note     none
* @retval   none
*/
u8 FaultUpdateSingle(FaultQueueElement_t * fault, u32 faultcode1, u32 faultcode2, u8  text1, u8  text2)
{
    u8 ret;
    bool append = FALSE;

    FAULT_QUEUE_ENTER_CRITICAL;
    //in case that arguments are all the same
    if (!((fault->faultCode1 == faultcode1) && (fault->faultCode2 == faultcode2)
        && (fault->errText1 == text1) && (fault->errText2 == text2)))
    {
        append = TRUE;
    }
    if (append)
    {
        fault->faultCode1 = faultcode1;
        fault->faultCode2 = faultcode2;
        fault->errText1 = text1;
        fault->errText2 = text2;
        fault->attr &= ~(ACK_BIT | CLR_BIT | SHOW_BIT);
        ret = 0;
    }
    else
    {
        ret = 0xFF;
    }
    FAULT_QUEUE_EXIT_CRITICAL;
    return ret;
}

/**
* @brief    Get last queue element 
* @param    queue: pointer to queue instance
* @note     none
* @retval   none
*/
FaultQueueElement_t * FaultQueueGetLast(FaultQueue_t * queue)
{
    FaultQueueElement_t * out = NULL;

    FAULT_QUEUE_ENTER_CRITICAL;
    if (queue->pFQIn == queue->pFQOut)
    {
        //no data
        out = NULL;
    }
    else
    {
        out = queue->pFQOut;
    }
    FAULT_QUEUE_EXIT_CRITICAL;
    return out;
}

/**
* @brief    Remove last queue element
* @param    queue: pointer to queue instance
* @note     none
* @retval   none
*/
u8 FaultQueueRemoveLast(FaultQueue_t * queue)
{
    u8 ret;
    FAULT_QUEUE_ENTER_CRITICAL;
    if (queue->pFQIn == queue->pFQOut)
    {
        ret = 0xFF;
    }
    else
    {
        queue->pFQOut->attr |= ACK_BIT; //need?
        queue->pFQOut ++;
        if (queue->pFQOut > &(queue->element[FAULT_QUEUE_SIZE - 1])) 
        {
            queue->pFQOut = &(queue->element[0]);
        }
        ret = 0;
    }
    FAULT_QUEUE_EXIT_CRITICAL;
    return ret;
}

void FaultQueueRemoveItem(FaultQueue_t * queue, u32 faultcode1, u32 faultcode2, u8  text1, u8  text2)
{
    u8 i;
    for(i = 0; i < FAULT_QUEUE_SIZE; i++)
    {
        if((queue->element[i].attr & ACK_BIT) == 0)
        {
            if((queue->element[i].errText1 == text1) && (queue->element[i].errText2 == text2) && (queue->element[i].faultCode1 == faultcode1) && (queue->element[i].faultCode2 == faultcode2))
            {
                queue->element[i].attr |= CLR_BIT;
                if(queue->element[i].faultAckAction != 0)
                {
                    queue->element[i].faultAckAction(queue->element[i].fault_index);
                }
                break;
            }
        }
    }
}

void FaultSingleQueueRemoveItem(FaultQueueElement_t * fault)
{
    if((fault->attr & ACK_BIT) == 0)
    {
        fault->attr |= CLR_BIT;
    }
}

/**
* @brief    Fault show in display
* @param    none
* @note     none
* @retval   none
*/
u8 FaultShow(void)
{
    FaultQueueElement_t * fault;
    FaultQueueElement_t currentSingleFault;
    FaultQueue_t * queue[2];
    u8 i;

    queue[0] = &FaultOutputQueue;
    queue[1] = &FaultQueue;

    for (i = 0; i < 2; i++)
    {
        fault = FaultQueueGetLast(queue[i]);
        if (fault != NULL)
        {
            //buzzer on
            if (fault->faultPreAckAction != 0)
            {
                fault->faultPreAckAction();
            }
            //LCD show only once
            LCDShowOnce = TRUE;
            MENUShowFault(fault);
            while ((fault->attr & ACK_BIT) == 0)
            {
                //wait for menu clear key push
                if (MENUClearKeyEvent(fault))
                {
                    fault->attr |= ACK_BIT;
                    fault->attr &= ~(CLR_BIT | SHOW_BIT);
                    if (fault->faultAckAction != 0)
                    {
                        fault->faultAckAction(fault->fault_index);
                    }

                    if (FaultQueueRemoveLast(queue[i]) != 0)
                    {
                        break;
                    }
                    reverse_distance_disp_flag = 0;
                }
        		task_status[UPDT_DISPLAY_ID] = ALIVE; 
                osDelay(UPDT_DISPLAY_ITV);
            }
        }  
    }

    memcpy(&currentSingleFault, &SingleFault, sizeof(SingleFault));
    while ((SingleFault.attr & ACK_BIT) == 0)
    {
        LCDShowOnce = TRUE;
        MENUShowFault(&SingleFault);
        while (TRUE)
        {      
            if ((currentSingleFault.faultCode1 != SingleFault.faultCode1) || (currentSingleFault.faultCode2 != SingleFault.faultCode2)
                 || (currentSingleFault.errText1 != SingleFault.errText1) || (currentSingleFault.errText2 != SingleFault.errText2))
            {
                memcpy(&currentSingleFault, &SingleFault, sizeof(SingleFault));
                break;
            }
            else
            {
                memcpy(&currentSingleFault, &SingleFault, sizeof(SingleFault));
            }
            //wait for menu clear key push
            if (MENUClearKeyEvent(&SingleFault))
            {
                SingleFault.attr  |= (ACK_BIT);
                SingleFault.faultCode1 = 0;
                SingleFault.faultCode2 = 0;
                SingleFault.errText1 = 0;
                SingleFault.errText2 = 0;
                SingleFault.attr &= ~(CLR_BIT | SHOW_BIT);
                reverse_distance_disp_flag = 0;
                break;
            }
    		task_status[UPDT_DISPLAY_ID] = ALIVE;
            osDelay(UPDT_DISPLAY_ITV);
        }
    }

  return 0;
}

void FaultPreAckActionIdle(void)
{
}

void FaultAckActionIdle(u8 fault_index)
{
    SetBuzzerOff();
}



